"""
This will eventually replace all the `to_dict` methods of the different model
classes we have. This will ensure cleaner code and better
separation of concerns.
"""
from funcy import project

from flask_login import current_user
from redash.authentication import current_org
from rq.job import JobStatus
from rq.timeouts import JobTimeoutException

from redash import models
from redash.permissions import has_access, view_only
from redash.utils import json_loads
from redash.models.parameterized_query import ParameterizedQuery


from .query_result import (
    serialize_query_result,
    serialize_query_result_to_dsv,
    serialize_query_result_to_xlsx,
    serialize_data_result_to_xlsx,
)


def public_widget(widget):
    res = {
        "id": widget.id,
        "width": widget.width,
        "options": json_loads(widget.options),
        "text": widget.text,
        "updated_at": widget.updated_at,
        "created_at": widget.created_at,
    }

    v = widget.visualization
    if v and v.id:
        res["visualization"] = {
            "type": v.type,
            "name": v.name,
            "query_id": v.query_id,
            "description": v.description,
            "options": json_loads(v.options),
            "updated_at": v.updated_at,
            "created_at": v.created_at,
            "query": {
                "id": v.query_rel.id,
                "name": v.query_rel.name,
                "description": v.query_rel.description,
                "options": v.query_rel.options,
            },
        }

    return res


def public_dashboard(dashboard):
    dashboard_dict = project(
        serialize_dashboard(dashboard, with_widgets=True, with_favorite_state=False),
        ("name", "layout", "widgets", "dashboard_filters_enabled", "updated_at", "created_at"),
    )

    # widget_list = (
    #     models.Widget.query.filter(models.Widget.dashboard_id == dashboard.id)
    #     .outerjoin(models.Visualization)
    #     .outerjoin(models.Query)
    # )

    # dashboard_dict["widgets"] = [public_widget(w) for w in widget_list]
    return dashboard_dict


class Serializer(object):
    pass


class QuerySerializer(Serializer):
    def __init__(self, object_or_list, **kwargs):
        self.object_or_list = object_or_list
        self.options = kwargs

    def serialize(self):
        if isinstance(self.object_or_list, models.Query):
            result = serialize_query(self.object_or_list, **self.options)
            if (
                self.options.get("with_favorite_state", True)
                and not current_user.is_api_user()
            ):
                result["is_favorite"] = models.Favorite.is_favorite(
                    current_user.id, self.object_or_list
                )
        else:
            result = [
                serialize_query(query, **self.options) for query in self.object_or_list
            ]
            if self.options.get("with_favorite_state", True):
                favorite_ids = models.Favorite.are_favorites(
                    current_user.id, self.object_or_list
                )
                for query in result:
                    query["is_favorite"] = query["id"] in favorite_ids

        return result


def serialize_query(
    query,
    with_stats=False,
    with_visualizations=False,
    with_user=True,
    with_last_modified_by=True,
    with_visualization_options=True,
):
    d = {
        "id": query.id,
        "latest_query_data_id": query.latest_query_data_id,
        "name": query.name,
        "description": query.description,
        "query": query.query_text,
        "query_hash": query.query_hash,
        "schedule": query.schedule,
        "api_key": query.api_key,
        "is_archived": query.is_archived,
        "is_draft": query.is_draft,
        "updated_at": query.updated_at,
        "created_at": query.created_at,
        "data_source_id": query.data_source_id,
        "data_source_name": query.data_source.name,
        "options": query.options,
        "version": query.version,
        "tags": query.tags or [],
        "is_safe": query.parameterized.is_safe,
    }

    if with_user:
        d["user"] = query.user.to_dict()
    else:
        d["user_id"] = query.user_id

    if with_last_modified_by:
        d["last_modified_by"] = (
            query.last_modified_by.to_dict()
            if query.last_modified_by is not None
            else None
        )
    else:
        d["last_modified_by_id"] = query.last_modified_by_id

    if with_stats:
        if query.latest_query_data is not None:
            d["retrieved_at"] = query.retrieved_at
            d["runtime"] = query.runtime
        else:
            d["retrieved_at"] = None
            d["runtime"] = None

    if with_visualizations:
        d["visualizations"] = [
            serialize_visualization(vis, with_query=False, with_options=with_visualization_options)
            for vis in query.visualizations
        ]

    return d


def serialize_visualization(object, with_query=True, with_options=True):
    if with_options:
        d = {
        "id": object.id,
        "type": object.type,
        "name": object.name,
        "query_id": object.query_id,
        "description": object.description,
        "options": json_loads(object.options),
        "template_parameters": object.template_parameters if hasattr(object, 'template_parameters') else None,
        "template_options": object.template_options if hasattr(object, 'template_options') else None,
        "updated_at": object.updated_at,
        "created_at": object.created_at,
        }
    else:
        d = {
        "id": object.id,
        "type": object.type,
        "name": object.name,
        "query_id": object.query_id,
        "description": object.description,
        "template_parameters": object.template_parameters if hasattr(object, 'template_parameters') else None,
        "template_options": object.template_options if hasattr(object, 'template_options') else None,
        "updated_at": object.updated_at,
        "created_at": object.created_at,
        }

    if with_query:
        d["query"] = serialize_query(object.query_rel, with_visualizations=True)

    return d


def serialize_widget(object, with_visualizations=True, with_options=True):
    if with_visualizations or with_options:
        d = {
            "id": object.id,
            "width": object.width,
            "options": json_loads(object.options),
            "dashboard_id": object.dashboard_id,
            "text": object.text,
            "template_type": object.template_type,
            "template_name": object.template_name,
            "template_logo": object.template_logo,
            "updated_at": object.updated_at,
            "created_at": object.created_at,
        }
    else:
        d = {
            "id": object.id,
            "width": object.width,
            "dashboard_id": object.dashboard_id,
            "text": object.text,
            "template_type": object.template_type,
            "template_name": object.template_name,
            "template_logo": object.template_logo,
            "updated_at": object.updated_at,
            "created_at": object.created_at,
        }

    if with_options and object.visualization and object.visualization.id:
        d["visualization"] = serialize_visualization(object.visualization)

    if with_visualizations and d["options"] and "widgetOptions" in d["options"]:
        option = d["options"]["widgetOptions"]

        if option:
            visualizations = {}
            toggles = ['toggle_view1','toggle_view2','toggle_view3','toggle_view4','toggle_view5']

            for toggle in toggles:
                toggle_view = option.get(toggle)
                if toggle_view and isinstance(toggle_view,dict):
                    #强制刷新
                    vis = models.Visualization.get_by_id(toggle_view["id"])
                    toggle_view = serialize_visualization(vis)
                    if "query" not in toggle_view.keys() and "query_id" in toggle_view.keys():
                        if ("options" in toggle_view and "compose" in toggle_view["options"] and len(toggle_view["options"]["compose"])>0):
                            toggle_view["query"] = serialize_query(models.Query.by_id(toggle_view["query_id"]), with_visualizations=True)
                        else:    
                            toggle_view["query"] = serialize_query(models.Query.by_id(toggle_view["query_id"]))
                    visualizations[toggle_view["id"]] = toggle_view

            d["visualizations"] = visualizations;

    return d


def serialize_alert(alert, full=True):
    d = {
        "id": alert.id,
        "name": alert.name,
        "options": alert.options,
        "state": alert.state,
        "last_triggered_at": alert.last_triggered_at,
        "updated_at": alert.updated_at,
        "created_at": alert.created_at,
        "rearm": alert.rearm,
    }

    if full:
        d["query"] = serialize_query(alert.query_rel)
        d["dashboard"] = serialize_dashboard(alert.dashboard_rel) if alert.dashboard_rel is not None else None
        d["user"] = alert.user.to_dict()
    else:
        d["query_id"] = alert.query_id
        d["dashboard_id"] = alert.dashboard_id if alert.dashboard_id is not None else None
        d["user_id"] = alert.user_id

    return d


def serialize_dashboard(obj, with_widgets=False, user=None, with_favorite_state=True):
    layout = json_loads(obj.layout)

    widgets = []

    if with_widgets:
        for w in obj.widgets:
            widgets.append(serialize_widget(w))
            # if w.visualization_id is None:
            # elif user and has_access(w.visualization.query_rel, user, view_only):
            #     widgets.append(serialize_widget(w))
            # else:
            #     widget = project(
            #         serialize_widget(w),
            #         (
            #             "id",
            #             "width",
            #             "dashboard_id",
            #             "options",
            #             "created_at",
            #             "updated_at",
            #         ),
            #     )
            #     widget["restricted"] = True
            #     widgets.append(widget)
    else:
        widgets = None

    d = {
        "id": obj.id,
        "slug": obj.name_as_slug,
        "name": obj.name,
        "user_id": obj.user_id,
        "user": {
            "id": obj.user.id,
            "name": obj.user.name,
            "email": obj.user.email,
            "mobile": obj.user.mobile,
            "profile_image_url": obj.user.profile_image_url,
        },
        "layout": layout,
        "dashboard_filters_enabled": obj.dashboard_filters_enabled,
        "widgets": widgets,
        "is_archived": obj.is_archived,
        "is_draft": obj.is_draft,
        "tags": obj.tags or [],
        "updated_at": obj.updated_at,
        "created_at": obj.created_at,
        "template_type": obj.template_type,
        "template_logo": obj.template_logo,
        "template_url": obj.template_url,
        "version": obj.version,
    }

    return d


class DashboardSerializer(Serializer):
    def __init__(self, object_or_list, **kwargs):
        self.object_or_list = object_or_list
        self.options = kwargs

    def serialize(self):
        if isinstance(self.object_or_list, models.Dashboard):
            result = serialize_dashboard(self.object_or_list, **self.options)
            if (
                self.options.get("with_favorite_state", True)
                and not current_user.is_api_user()
            ):
                result["is_favorite"] = models.Favorite.is_favorite(
                    current_user.id, self.object_or_list
                )
        else:
            result = [
                serialize_dashboard(obj, **self.options) for obj in self.object_or_list
            ]
            if self.options.get("with_favorite_state", True):
                favorite_ids = models.Favorite.are_favorites(
                    current_user.id, self.object_or_list
                )
                for obj in result:
                    obj["is_favorite"] = obj["id"] in favorite_ids

        return result


def serialize_job(job):
    # TODO: this is mapping to the old Job class statuses. Need to update the client side and remove this
    STATUSES = {
        JobStatus.QUEUED: 1,
        JobStatus.STARTED: 2,
        JobStatus.FINISHED: 3,
        JobStatus.FAILED: 4,
    }

    job_status = job.get_status()
    if job.is_started:
        updated_at = job.started_at or 0
    else:
        updated_at = 0

    status = STATUSES[job_status]
    result = query_result_id = None

    if job.is_cancelled:
        error = "Query cancelled by user."
        status = 4
    elif isinstance(job.result, Exception):
        error = str(job.result)
        status = 4
    elif isinstance(job.result, dict) and "error" in job.result:
        error = job.result["error"]
        status = 4
    else:
        error = ""
        result = query_result_id = job.result

    return {
        "job": {
            "id": job.id,
            "updated_at": updated_at,
            "status": status,
            "error": error,
            "result": result,
            "query_result_id": query_result_id,
        }
    }
